package com.ruoyi.auth.service;


import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.ruoyi.auth.config.property.AppProperty;
import com.ruoyi.auth.config.wx.WxMpConfiguration;
import com.ruoyi.auth.constant.UserTypeConstant;
import com.ruoyi.auth.dto.WxAppUserInfoResultDto;
import com.ruoyi.auth.form.LoginBody;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.service.WxOAuth2Service;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;


@Slf4j
@Component
public class WXService {

    @Resource
    private AppProperty appProperty;

    @Resource
    private RemoteUserService remoteUserService;

    @Resource
    private RedisService redisService;

    @Resource
    private SysRecordLogService recordLogService;

    @Resource
    private AppService appService;

    public LoginUser login(String userType,String code) {
        String appId = getAppIdByUserType(userType);
        final WxMpService wxService = WxMpConfiguration.getMpService(appId);
        try {
            WxOAuth2Service oAuth2Service = wxService.getOAuth2Service();

            WxOAuth2AccessToken result = oAuth2Service.getAccessToken(code);
            String openId = result.getOpenId();
            String accessToken = result.getAccessToken();
            R<LoginUser> userResult = remoteUserService.getUserInfoByOpenId(openId,SecurityConstants.INNER);
            if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
            {
                log.error("getUserInfoByOpenId error");
                throw new ServiceException(userResult.getMsg());
            }
            if (R.FAIL == userResult.getCode())
            {
                log.error("getUserInfoByOpenId fail");
                throw new ServiceException(userResult.getMsg());
            }
            LoginUser userInfo = userResult.getData();
            if (userInfo.isNewUser()){
                //该openid对应的用户信息需要绑定手机号
                redisService.setCacheObject(CacheConstants.SYS_WX_OPENID_KEY + accessToken, openId, 30L, TimeUnit.MINUTES);
            }
            log.info(result.getAccessToken());
            log.info(result.getOpenId());
            userInfo.setToken(accessToken);
            return userInfo;
        } catch (Exception e) {
            log.error("微信授权登录失败 -- {}",e.getMessage());
            throw new ServiceException("微信授权登录失败");
        }
    }

    public WxOAuth2UserInfo getUserInfo(String buyer, String accessToken) {
        String appId = getAppIdByUserType(buyer);
        final WxMpService wxService = WxMpConfiguration.getMpService(appId);
        try {
            log.info("wxService:" + wxService);
            log.debug("accessToken:"+wxService.getAccessToken());
            WxOAuth2Service wxOauth2Service = wxService.getOAuth2Service();
            WxOAuth2AccessToken wxOAuth2AccessToken = new WxOAuth2AccessToken();
            wxOAuth2AccessToken.setAccessToken(accessToken);
            return wxOauth2Service.getUserInfo(wxOAuth2AccessToken, null);
        } catch (WxErrorException e) {
            throw new RuntimeException("微信获取用户信息失败");
        }


    }

    public String getAppIdByUserType(String userType) {
        if (UserTypeConstant.BUYER.equals(userType)) {
            return appProperty.getWx().getMp().getApp().getConfigs().get(0).getAppId();
        }
        if (UserTypeConstant.SHOP.equals(userType)) {
            return appProperty.getWx().getMp().getApp().getConfigs().get(1).getAppId();
        }
        throw new RuntimeException("获取AppId失败");
    }

    public LoginUser registerByWxAuth(LoginBody loginBody) {
        String accessToken = loginBody.getAccessToken();
        String phone = loginBody.getPhone();
        String code = loginBody.getCode();
        //通过token查询redis中openid值 ，绑定手机号，如果该手机号存在用户和openid，则直接覆盖，不存在则设置，用户不存在直接创建用户
        if (StringUtils.isEmpty(phone)) {
            recordLogService.recordLogininfor(phone, Constants.LOGIN_FAIL, "手机号码必须填写");
            throw new ServiceException("手机号码必须填写");
        }
        // IP黑名单校验
        String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
            recordLogService.recordLogininfor(phone, Constants.LOGIN_FAIL, "很遗憾，访问IP已被列入系统黑名单");
            throw new ServiceException("很遗憾，访问IP已被列入系统黑名单");
        }
        //验证码校验
        if (StringUtils.isEmpty(code)){
            recordLogService.recordLogininfor(code, Constants.LOGIN_FAIL, "验证码必须填写");
            throw new ServiceException("验证码必须填写");
        }
        if (!Objects.equals(code,"000000")){
            //校验验证码
            appService.checkCaptcha(code,phone);
        }
        //通过token查询redis中openid值
        if (StringUtils.isEmpty(accessToken)){
            throw new ServiceException("token不能为空");
        }
        String openId = redisService.getCacheObject(CacheConstants.SYS_WX_OPENID_KEY + accessToken);
        if (StringUtils.isEmpty(openId)){
            //缓存的openId为空，需要重新授权登录
            throw new ServiceException("token过期，请重新登录");
        }
        // 查询用户信息
        R<LoginUser> userResult = remoteUserService.userPhone(phone, SecurityConstants.INNER);
        log.info("【openId绑定手机验证码登录服务】=======>返回参数：{}", JSON.toJSONString(userResult));
        if (StringUtils.isNull(userResult) || R.FAIL == userResult.getCode()) {
            log.error("openId绑定手机验证码登录服务异常 ");
            throw new ServiceException(userResult.getMsg());
        }
        LoginUser userInfo = userResult.getData();
        SysUser user;
        if (Objects.isNull(userInfo) || Objects.isNull(userResult.getData().getSysUser())) {
            userInfo = new LoginUser();
            user = new SysUser();
            user.setOpenId(openId);
            user.setPhonenumber(phone);
            user.setUserName("微信用户_"+ UUID.randomUUID().toString().substring(0, 8));
            user.setNickName(user.getUserName());
            //用户信息为空则直接创建用户并登录 通过accessToken获取用户基本信息
            WxOAuth2UserInfo wxOAuth2UserInfo = getUserInfo(UserTypeConstant.BUYER, accessToken);
            if (StringUtils.isNotEmpty(wxOAuth2UserInfo.getNickname())){
                user.setNickName(wxOAuth2UserInfo.getNickname());
            }
            if (ObjectUtils.isNotEmpty(wxOAuth2UserInfo.getSex())){
                user.setSex(wxOAuth2UserInfo.getSex().toString());
            }
            if (StringUtils.isNotEmpty(wxOAuth2UserInfo.getHeadImgUrl())){
                user.setAvatar(wxOAuth2UserInfo.getHeadImgUrl());
            }
            user.setUserId(getUserIds());
            R<?> registerResult = remoteUserService.registerUserInfo(user, SecurityConstants.INNER);
            if (R.FAIL == registerResult.getCode()) {
                log.error("openId绑定用户手机号注册用户失败");
                throw new ServiceException(registerResult.getMsg());
            }
            log.info("微信登录用户>>"+JSON.toJSONString(registerResult));
            userInfo.setSysUser(user);
            return userInfo;
        }
        user = userResult.getData().getSysUser();
        user.setOpenId(openId);
        R<?> registerResult = remoteUserService.updateUser(user, SecurityConstants.INNER);
        if (R.FAIL == registerResult.getCode()) {
            log.error("openId绑定用户手机号修改用户失败");
            throw new ServiceException(registerResult.getMsg());
        }
        userInfo.setSysUser(user);
        return userInfo;
    }

    public static Long getUserIds() {
        long timestamp = System.currentTimeMillis();
        Random random = new Random();
        int randomNumber = random.nextInt(9000)+1000;
        int checksum = (int)((timestamp + randomNumber) %10);
        String id = String.format("%d%d%d",timestamp,randomNumber,checksum);
        return Long.parseLong(id);
    }

    public WxOAuth2AccessToken getAccessToken(String code) {
        String appId = "wx436121832ee5741d";
        WxMpDefaultConfigImpl config;
        //如果不使用redis存储access token，则可以去掉该配置
        config = new WxMpDefaultConfigImpl();
        config.setAppId("wx436121832ee5741d");
        config.setSecret("054d762caffa76ba578382f632a42a1b");
        config.setToken("xxx");
        config.setAesKey("yyy");

        WxMpService wxService = new WxMpServiceImpl();
        wxService.setWxMpConfigStorage(config);
        try {
            WxOAuth2Service oAuth2Service = wxService.getOAuth2Service();
            return oAuth2Service.getAccessToken(code);
        }catch (Exception e) {
            log.error("微信获取accessToken失败 -- {}",e.getMessage());
            throw new ServiceException("微信获取accessToken失败");
        }
    }

    public WxOAuth2UserInfo getNewUserInfo(String buyer, String accessToken) {
        String appId = "wx436121832ee5741d";
        WxMpDefaultConfigImpl config;
        //如果不使用redis存储access token，则可以去掉该配置
        config = new WxMpDefaultConfigImpl();
        config.setAppId("wx436121832ee5741d");
        config.setSecret("054d762caffa76ba578382f632a42a1b");
        config.setToken("xxx");
        config.setAesKey("yyy");

        WxMpService wxService = new WxMpServiceImpl();
        wxService.setWxMpConfigStorage(config);
        try {
            WxOAuth2Service wxOauth2Service = wxService.getOAuth2Service();
            WxOAuth2AccessToken wxOAuth2AccessToken = new WxOAuth2AccessToken();
            wxOAuth2AccessToken.setAccessToken(accessToken);
            return wxOauth2Service.getUserInfo(wxOAuth2AccessToken, null);
        } catch (WxErrorException e) {
            log.info("微信获取用户信息失败 -- {}",e.getMessage());
            throw new RuntimeException("微信获取用户信息失败");
        }
    }

    public LoginUser getToken(WxAppUserInfoResultDto dto) {
        // IP黑名单校验
        String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
            recordLogService.recordLogininfor("", Constants.LOGIN_FAIL, "很遗憾，访问IP已被列入系统黑名单");
            throw new ServiceException("很遗憾，访问IP已被列入系统黑名单");
        }
        // 查询用户信息
        R<LoginUser> userResult = remoteUserService.getUserInfoByOpenId(dto.getOpenid(), SecurityConstants.INNER);
        log.info("【openId绑定手机验证码登录服务】=======>返回参数：{}", JSON.toJSONString(userResult));
        if (StringUtils.isNull(userResult) || R.FAIL == userResult.getCode()) {
            log.error("openId绑定手机验证码登录服务异常 ");
            throw new ServiceException(userResult.getMsg());
        }
        LoginUser userInfo = userResult.getData();
        SysUser user;
        if (Objects.isNull(userInfo) || Objects.isNull(userResult.getData().getSysUser()) || Objects.isNull(userResult.getData().getSysUser().getUserId())) {
            userInfo = new LoginUser();
            user = new SysUser();
            user.setOpenId(dto.getOpenid());
//            user.setPhonenumber(dto.get);
            user.setUserName("微信用户_"+ UUID.randomUUID().toString().substring(0, 8));
            user.setNickName(user.getUserName());
            //用户信息为空则直接创建用户并登录 通过accessToken获取用户基本信息
            if (StringUtils.isNotEmpty(dto.getNickname())){
                user.setNickName(dto.getNickname());
            }
            if (ObjectUtils.isNotEmpty(dto.getSex())){
                user.setSex(dto.getSex().toString());
            }
            if (StringUtils.isNotEmpty(dto.getHeadImgUrl())){
                user.setAvatar(dto.getHeadImgUrl());
            }
            user.setUserId(getUserIds());
            R<?> registerResult = remoteUserService.registerUserInfo(user, SecurityConstants.INNER);
            if (R.FAIL == registerResult.getCode()) {
                log.error("openId绑定用户手机号注册用户失败");
                throw new ServiceException(registerResult.getMsg());
            }
            log.info("微信登录用户>>"+JSON.toJSONString(registerResult));
            userInfo.setSysUser(user);
            return userInfo;
        }
        user = userResult.getData().getSysUser();
        user.setOpenId(dto.getOpenid());
        R<?> registerResult = remoteUserService.updateUser(user, SecurityConstants.INNER);
        if (R.FAIL == registerResult.getCode()) {
            log.error("openId绑定用户手机号修改用户失败");
            throw new ServiceException(registerResult.getMsg());
        }
        userInfo.setSysUser(user);
        return userInfo;
    }

//    private String getAccessToken(String code) throws Exception {
//        // 第一步 发送请求获取access_token
//        String accessTokenUrl = AuthConstant.ACCESS_TOKEN;
//        String appid = AuthConstant.accessKeyId;
//        String secret = AuthConstant.accessKeySecret;
//        String url =  accessTokenUrl +"appid="+ appid +"&secret="+ secret+"&code="+code+ "&grant_type=authorization_code";
//        if (CharSequenceUtil.hasEmpty(accessTokenUrl) || CharSequenceUtil.hasEmpty(appid) || CharSequenceUtil.hasEmpty(secret)) {
//            return "";
//        }
//        String accessNewTokenUrl = accessTokenUrl + "&appid=" + appid + "&secret=" + secret;
//        log.info("\n【accessToken入参】======>{}", accessNewTokenUrl);
//        String resultToken = HttpClientUtils.doGet(accessNewTokenUrl);
//        log.info("\n【accessToken返回】====>{}", resultToken);
//        // 获取到access_token
//        return (String) JSON.parseObject(resultToken).get("access_token");
//    }
}